/*
 * The MIT License
 *
 * Copyright 2012 http://generic-toolset.googlecode.com.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.rlpw.swing;

import java.util.Collection;
import java.util.EventObject;
import java.util.LinkedList;
import java.util.List;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import org.rlpw.swing.event.ModelEvent;
import org.rlpw.swing.event.ModelListener;
import org.rlpw.swing.event.ModelReplacedEvent;
import org.rlpw.util.UniqueList;

/**
 *
 * @param <T> the type of the elements
 */
public class DefaultModel<T> extends UniqueList<T> implements Model<T> {

    private List<View<T>> viewlist = new UniqueList<>();
    private List<ModelListener<T>> modelListeners = new UniqueList<>();
    private List<ListDataListener> dataListeners = new UniqueList<>();

    public DefaultModel() {
    }

    public DefaultModel(Collection<T> c) {
        super(c);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void updateViews() {
        for (View<T> v : viewlist) {
            v.updateView();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addView(View<T> view) {
        if (view == null) {
            return;
        }
        viewlist.add(view);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void removeView(View<T> view) {
        if (view == null) {
            return;
        }
        viewlist.remove(view);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean add(T e) {
        int index = size();
        if (super.add(e)) {
            updateViews();
            fireAdded(e, index);
            fireIntervalAdded(this, index, index);
            return true;
        }
        return false;

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void add(int index, T element) {
        super.add(index, element);
        updateViews();
        fireIntervalAdded(this, index, index);
        fireAdded(element, index);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean addAll(int index, Collection<? extends T> c) {
        if (super.addAll(index, c)) {
            if (!c.isEmpty()) {
                updateViews();
                fireIntervalAdded(this, index, index + c.size() - 1);
                int i = 0;
                for (T item : c) {
                    fireAdded(item, index + i++);
                }

            }
            return true;
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean addAll(Collection<? extends T> c) {
        return addAll(size(), c);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void clear() {
        int index = size() - 1;
        super.clear();
        if (index >= 0) {
            updateViews();
            fireIntervalRemoved(this, 0, index);
            fireAllRemoved();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T remove(int index) {
        T item = super.remove(index);
        updateViews();
        fireRemoved(item, index);
        fireIntervalRemoved(this, index, index);
        return item;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean remove(Object o) {
        int index = indexOf(o);
        if (super.remove((T) o)) {
            updateViews();
            fireIntervalRemoved(this, index, index);
            fireRemoved((T) o, index);
            return true;
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void removeRange(int fromIndex, int toIndex) {
        List<T> removed = new LinkedList<>();
        boolean range = true;
        for (int i = fromIndex; i <= toIndex; i++) {
            if (!remove(get(i))) {
                range = false;
            }
        }
        updateViews();
        if (range) {
            fireIntervalRemoved(this, fromIndex, toIndex);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean removeAll(Collection<?> c) {
        boolean b = true;
        for (Object o : c) {
            b |= remove((T) o);
        }
        return b;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean retainAll(Collection<?> c) {
        boolean b = true;
        for (T item : this) {
            if (!c.contains(item)) {
                b |= remove(item);
            }
        }
        return b;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public T set(int index, T element) {
        T item = super.set(index, element);
        updateViews();
        fireReplaced(item, element, index);
        fireContentsChanged(this, index, index);
        return item;
    }

    /**
     * Notifies all listeners that have registered interest for notification on
     * this event type.<br/>
     */
    protected synchronized void fireChanged(T item, int index) {
        ModelEvent<T> event = new ModelEvent<>(this, item, index);
        for (ModelListener<T> l : modelListeners) {
            l.itemChanged(event);
        }
    }

    /**
     * Notifies all listeners that have registered interest for notification on
     * this event type.<br/>
     */
    protected synchronized void fireAdded(T item, int index) {
        ModelEvent<T> event = new ModelEvent<>(this, item, index);
        for (ModelListener<T> l : modelListeners) {
            l.itemAdded(event);
        }
    }

    /**
     * Notifies all listeners that have registered interest for notification on
     * this event type.<br/>
     */
    protected synchronized void fireRemoved(T item, int index) {
        ModelEvent<T> event = new ModelEvent<>(this, item, index);
        for (ModelListener<T> l : modelListeners) {
            l.itemRemoved(event);
        }
    }

    /**
     * Notifies all listeners that have registered interest for notification on
     * this event type.<br/>
     */
    protected synchronized void fireReplaced(T oldItem, T newItem, int index) {
        ModelReplacedEvent<T> event = new ModelReplacedEvent<>(this, oldItem, newItem, index);
        for (ModelListener<T> l : modelListeners) {
            l.itemReplaced(event);
        }
    }

    /**
     * Notifies all listeners that have registered interest for notification on
     * this event type.<br/>
     */
    protected synchronized void fireAllRemoved() {
        for (ModelListener<T> l : modelListeners) {
            l.allItemsRemoved(new EventObject(this));
        }
    }

    /**
     * Notifies all listeners that have registered interest for notification on
     * this event type.<br/>
     */
    protected synchronized void fireContentsChanged(Object source, int index0, int index1) {
        ListDataEvent event = new ListDataEvent(source, ListDataEvent.CONTENTS_CHANGED, index0, index1);
        for (ListDataListener l : dataListeners) {
            l.contentsChanged(event);
        }
    }

    /**
     * Notifies all listeners that have registered interest for notification on
     * this event type.<br/>
     */
    protected synchronized void fireIntervalAdded(Object source, int index0, int index1) {
        ListDataEvent event = new ListDataEvent(source, ListDataEvent.INTERVAL_ADDED, index0, index1);
        for (ListDataListener l : dataListeners) {
            l.intervalAdded(event);
        }
    }

    /**
     * Notifies all listeners that have registered interest for notification on
     * this event type.<br/>
     */
    protected synchronized void fireIntervalRemoved(Object source, int index0, int index1) {
        ListDataEvent event = new ListDataEvent(source, ListDataEvent.INTERVAL_REMOVED, index0, index1);
        for (ListDataListener l : dataListeners) {
            l.intervalRemoved(event);
        }


    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addModelListener(ModelListener<T> l) {
        if (l != null) {
            modelListeners.add(l);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void removeModelListener(ModelListener<T> l) {
        modelListeners.remove(l);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addListDataListener(ListDataListener l) {
        if (l != null) {
            dataListeners.add(l);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void removeListDataListener(ListDataListener l) {
        dataListeners.remove(l);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<View<T>> getViews() {
        return viewlist;
    }
}
